home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
yerk
/
mps231ss.hqx
/
Mops source
/
Toolbox classes
/
View
< prev
next >
Wrap
Text File
|
1993-02-25
|
13KB
|
366 lines
\ VIEW class.
\ Oct 91 mrh Initial version.
\ May 92 mrh Support for "new-style" controls
\ Feb 93 mrh Added IDLE: method
\ VIEW is the generic superclass for everything that can be drawn in a
\ window. For example, all controls are now drawn in a view, rather than
\ in a window as such, although every view must have an owning window.
\ This idea is lifted from MacApp and TCL, except that in Mops a Window
\ itself isn't a view, but contains one special view (the ContView) which
\ covers the whole drawing area of the window (excluding any scroll bars).
\ In the view, we have an ivar which is a rect, ViewRect. This is the
\ rectangle defining the outer boundary of this view, relative to the
\ current grafPort. This rect is used by its owning view to set the clip
\ region and the coordinate origin before calling any method on the view.
need ctl
\ Here we define the modes for subview corner points when the superview
\ is resized. So far we have Anchored, which means the point stays the
\ same distance from the corresponding superview point, Proportional,
\ which means the point stays proportionately at the same position along
\ the superview's edge, and Floating, which means that the subview edge
\ stays the same length, and this point has its position dictated by what
\ its "partner" does. If both are Floating, then the centre point of the
\ edge will stay in the same proportional position.
type{ anchored proportional floating }
0 value MPOINT
\ Utility words
: NOCLIP 0 0 32766 dup put: tempRect addr: tempRect call ClipRect ;
: CTLEXEC \ ( part# ctlHndl -- ) Executes action for control.
get-ctl-obj exec: ** ;
\ CtlProc is the procedure to be executed when a control is being tracked.
:proc CTLPROC \ ( ctlHndl int:part -- )
word0 swap ctlExec ;proc
\ CTLHIT? looks for a control click.
: CTLHIT? { wind \ part ^ctl action1 action2 -- b }
where: fEvent g->l -> mpoint \ save mouse loc
word0 mpoint wind theCtl call FindControl
word0 -> part
theCtl @ -> ^ctl \ ctl handle
part
CASE[ inThumb ], [ inCheckBox ], [ inButton ]=>
\ Only exec after mouseUp
0 -> action1 \ 0 since gets passed to TrackControl
['] ctlExec -> action2
DEFAULT=>
drop ['] ctlproc -> action1 ['] 2drop -> action2
]CASE
^ctl
IF word0 ^ctl mpoint action1 call TrackControl word0
^ctl action2 execute true
ELSE false
THEN ;
\ Class PtrList is used for a list of pointers which needs to be expandable.
\ We will use this to implement a view's list of its subviews, and also
\ its list of controls. We may eventually migrate it back into Mops.dic if
\ it turns out to be useful enough. Also we don't have a REMOVE: method
\ yet -- put it in if you need it!
:class PTRLIST super{ string sequence }
:m ADD: \ ( ptr -- )
pad ! pad 4 add: super ;m
:m FIRST?:
size: super nif false exit then \ No elements - return false
reset: super ^1st: super @ true ;m
:m NEXT?: \ ( -- ptr T | -- F )
4 skip: super len: super NIF false exit THEN
^1st: super @ true ;m
;class
\ ==============================
:class VIEW super{ object } general
rect VIEWRECT \ Bounding rectangle, rel to grafport.
ptr ^MyVIEW \ Points to containing view
ptr ^MyWIND \ Points to owning window
ptrList SUBVIEWS \ List of views that this one contains
ptrList CONTROLS \ List of controls for this view
x-addr DRAW \ Draw handler
x-addr ClickHndlr \ We call this for a click
bool ALIVE?
bool ENABLED?
bool WantsClicks? \ True if we can accept clicks
bool SetClip? \ True if we need to set the clip (default)
byte #updates \ Counts number of pending updates
byte Tmode \ Modes for 4 corner points - these
byte Bmode \ control the behaviour when
byte Lmode \ the containing view is resized.
byte Rmode
:m GETRECT: get: viewRect ;m
:m GET^RECT: addr: viewRect ;m
:m ENABLED?: get: enabled? ;m
:m WINDOW: get: ^MyWind ;m
:m SETWINDOW: put: ^MyWind ;m
:m WANTSCLICKS: put: wantsClicks? ;m
:m SETCLICK: put: ClickHndlr true put: wantsClicks? ;m
:m SETDRAW: put: draw ;m
:m SETMODES: put: Bmode put: Rmode put: Tmode put: Lmode ;m
:m SUPERVIEW: get: ^MyView ;m
:m INIT: \ ( left top rt bot -- ) Parms give the bounding rectangle
\ for the view, relative to the owning view, or relative
\ to the owning window if this isn't a subview.
put: viewRect ;m
\ ADDVIEW: adds the passed-in view to this view's list of subviews,
\ and ADDCTL: adds a control to this view's list of controls. These must
\ be called at run time, since pointers are used, and also they have to be
\ called before NEW:.
:m ADDVIEW: ( ^view -- ) add: subviews ;m
:m ADDCTL: ( ^ctl -- ) add: controls ;m
\ NEW: ( ^oview -- ) fires up the view. ^oView is the owning view if
\ this is a subview, nilP otherwise. This method in normally called
\ automatically when NEW: is called on the owning window.
:m NEW: \ ( ^oview -- )
put: ^myView
nil?: ^myView
NIF window: [ get: ^myView ] put: ^myWind
THEN
addr: viewRect call ClipRect
BEGIN ^base each: controls WHILE new: [] REPEAT drop
BEGIN ^base each: subviews WHILE new: [] REPEAT drop
noClip true put: alive? ;m
:m RELEASE:
BEGIN each: subviews WHILE release: [] REPEAT
BEGIN each: controls WHILE release: [] REPEAT
false put: alive? ;m
private
:m (ADJ): { soStrt soEnd snStrt snEnd myStrt myEnd strtMode endMode
\ myLen soLen snLen adj -- myStrt myEnd }
soEnd soStrt - -> soLen
snEnd snStrt - -> snLen
myEnd myStrt - -> myLen
\ First we handle the pure displacement component, before we
\ worry about the size change.
snStrt soStrt - dup ++> myStrt ++> myEnd
\ Now we handle the size change, depending on the modes.
\ First we check for both sides Floating, as this is a special
\ case.
strtMode floating = endMode floating = and
IF snLen soLen - 2/ dup ++> myStrt ++> myEnd
ELSE
\ For the other cases, we set myStrt and myEnd as for Anchored
\ mode, then if either is different, we adjust.
snLen soLen - -> adj
adj ++> myEnd
strtMode proportional =
IF adj myStrt snStrt - soLen */ ++> myStrt THEN
endMode proportional =
IF adj snEnd myEnd - soLen */ --> myEnd THEN
strtMode floating =
IF myEnd myLen - -> myStrt THEN
endMode floating =
IF myStrt myLen + -> myEnd THEN
THEN
myStrt myEnd ;m
public
:m ADJUSTSIZE: { snL snT snR snB \ myL myT myR myB soL soT soR soB -- }
\ This method adjusts the size of the view, in accordance with a resize
\ of the superview, taking into account the modes Tmode, Bmode etc.
\ We allow a repositioning of the superview to occur as well, as this
\ simplifies things when dealing with subviews containing subviews.
\ snL etc. are the new coordinates of the superview's viewRect. We assume
\ the old values are still in the viewRect itself. Here we copy them into
\ soL, soT, soR and soB. This method is really only public since we late
\ bind to it.
get: viewRect -> myB -> myR -> myT -> myL \ My viewRect
getRect: [ get: ^myView ] -> soB -> soR -> soT -> soL
\ Superview's old viewRect
soL soR snL snR myL myR get: Lmode get: Rmode (adj): self
-> myR -> myL
soT soB snT snB myT myB get: Tmode get: Bmode (adj): self
-> myB -> myT
begin myL myT myR myB each: subviews
while adjustSize: []
repeat 2drop 2drop
myL myT myR myB put: viewRect ;m
:m SETRECT: { left top rt bot -- }
begin left top rt bot each: subviews while adjustSize: []
repeat 2drop 2drop
left top rt bot put: viewRect ;m
\ (SHIFT): ( dx dy -- ) does the housekeeping for a shift of the view
\ by the given distance. It adjusts the viewRect and calls (shift):
\ on all the subviews. I was planning to eventually implement SHIFT:
\ which would actually move the view's screen image as well, but now I
\ think this is covered by the PAN: method of class Scroller.
:m (SHIFT): { dx dy \ left top rt bot -- }
\ Shifts the view the given distance. Doesn't draw anything.
get: viewRect -> bot -> rt -> top -> left
left dx + top dy + rt dx + bot dy + put: viewRect
begin dx dy each: subviews while (shift): [] repeat 2drop ;m
:m MOVE: { x y \ oldL oldT newL newT -- }
\ Moves the view so that its top left corner is at
\ (x,y) relative to the owning view. Keeps subviews in their
\ same relative position.
getTop: viewRect -> oldT -> oldL \ Where we are now
getRect: [ get: ^myView ] 2drop
-> newT -> newL
x ++> newL y ++> newT \ Where we're going
newL oldL - newT oldT - (shift): self ;m
\ DRAW: is the method called to get the view to draw itself. There
\ are a few subtleties. Before drawing is done, we set the clip region
\ to viewRect, and then set the origin so that the top left corner of
\ viewRect will be (0, 0). Both MacApp and TCL do the equivalent, so I
\ guess it's a good idea. Then after drawing, we need to call draw:
\ for all the subviews. Now here's the good part. Both these jobs can
\ be done via the CallFirst/CallLast mechanism, so the DRAW: method itself
\ can just do the drawing. Here in the View class itself, this just
\ consists of executing the draw handler.
\ Another useful point: when the draw handler is executed, tempRect will
\ contain the bounding rectangle for the drawing, relative to the current
\ origin. This can be used to draw a frame, for example.
\ Final note: we DON'T clear the drawing area before calling the draw
\ handler. If you need it cleared, do it in the draw handler.
private
:m SetTempRect: { \ left top rt bot -- }
\ Sets tempRect to a view-relative version of viewRect
\ -- we use this for a number of things.
get: viewRect -> bot -> rt -> top -> left
0 0 rt left - bot top - put: tempRect ;m
public
\ SETCLIP: sets the clip before drawing. This is a rather elaborate
\ process, since we need to set the clip to the intersection of this
\ view's viewRect and all its superviews' viewRects (which could possibly
\ be smaller). This can all be inhibited by setting SetClip? false
\ (which we do when scrolling, for example, since the system has kindly
\ set the clip for us already).
\ Note: when this method is called, the origin has been set so that the
\ top left of this view is (0,0). This is because we're going to use
\ this origin for the drawing, and unless we use the same when we set the
\ clip, the clip rectangle gets translated away somewhere strange!
\ This method has to be public since we late-bind to it.
:m SetClip: { \ ^view oLeft oTop left top rt bot -- }
\ Note: origin is rel to this view.
get: setClip? nif true put: setClip? exit then
get: viewRect -> bot -> rt -> top -> left
left -> oLeft top -> oTop \ For origin adjustment later
get: ^myView -> ^view
BEGIN ^view nilP <>
WHILE ^view getRect: view \ Slight kludge to ensure we get
\ the viewRect itself, excluding
\ any scroll bars.
bot min -> bot rt min -> rt top max -> top left max -> left
^view superView: view -> ^view
REPEAT
left oLeft - top oTop - rt oLeft - bot oTop - put: tempRect
addr: tempRect call ClipRect ;m
private
:m SetupDraw: { \ left top rt bot -- }
get: ^myWind set: window \ Make sure we draw in the right window!
0 call SetOrigin
get: viewRect -> bot -> rt -> top -> left
left negate top negate pack call SetOrigin
get: setClip?
IF setClip: [self] else true put: setClip? THEN
setTempRect: self ;m
:m WindupDraw:
BEGIN each: controls WHILE draw: [] REPEAT
BEGIN each: subviews WHILE draw: [] REPEAT
0 call SetOrigin
0 put: #updates noClip ;m
public
callFirst setupDraw:
callLast windupDraw:
:m DRAW: exec: draw ;m
:m IDLE: \ Can be used in subview to call TEidle or whatever.
BEGIN each: subviews WHILE idle: ** REPEAT ;m
:m CLICK: \ ( -- b ) Returns true if we've handled the click.
get: wantsClicks? NIF false EXIT THEN
BEGIN each: subviews
WHILE click: ** IF uneach: subviews true EXIT THEN
REPEAT
\ OK, the click wasn't in any of my subviews, but was it
\ in my own area?
0 where: fEvent g->l addr: viewRect call PtInRect
IF exec: clickHndlr true
ELSE false
THEN ;m
:m KEY: \ ( c -- )
BEGIN dup each: subviews WHILE key: ** REPEAT drop ;m
:m ENABLE:
true put: enabled?
BEGIN each: subviews WHILE enable: ** REPEAT ;m
:m DISABLE:
false put: enabled?
BEGIN each: subviews WHILE disable: ** REPEAT ;m
:m CLASSINIT:
true put: wantsClicks? true put: setClip?
proportional dup 2dup setModes: self ;m
;class
endload